import {
  ActionIcon,
  Button,
  Center,
  Grid,
  Group,
  Image,
  Input,
  NumberInput,
  SimpleGrid,
  Stack,
  Switch,
  Text,
  Textarea,
  TextInput,
} from '@mantine/core'
import { DateTimePicker } from '@mantine/dates'
import { Dropzone } from '@mantine/dropzone'
import { useClipboard, useInputState } from '@mantine/hooks'
import { useModals } from '@mantine/modals'
import { notifications, showNotification, updateNotification } from '@mantine/notifications'
import { mdiCheck, mdiClipboard, mdiClose, mdiContentSaveOutline, mdiDeleteOutline, mdiDiceMultiple } from '@mdi/js'
import { Icon } from '@mdi/react'
import dayjs from 'dayjs'
import localizedFormat from 'dayjs/plugin/localizedFormat'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate, useParams } from 'react-router'
import { SwitchLabel } from '@Components/admin/SwitchLabel'
import { WithGameEditTab } from '@Components/admin/WithGameEditTab'
import { getInputNumber, randomInviteCode, showErrorMsg, tryGetErrorMsg } from '@Utils/Shared'
import { IMAGE_MIME_TYPES } from '@Utils/Shared'
import { useAdminGame } from '@Hooks/useGame'
import api, { GameInfoModel } from '@Api'
import misc from '@Styles/Misc.module.css'

dayjs.extend(localizedFormat)

const GameInfoEdit: FC = () => {
  const { id } = useParams()
  const numId = parseInt(id ?? '-1')
  const { game: gameSource, mutate } = useAdminGame(numId)
  const [game, setGame] = useState<GameInfoModel>()
  const navigate = useNavigate()

  const [disabled, setDisabled] = useState(false)
  const [start, setStart] = useInputState(dayjs())
  const [end, setEnd] = useInputState(dayjs())
  const [wpddl, setWpddl] = useInputState(3)

  const modals = useModals()
  const clipboard = useClipboard()

  const { t } = useTranslation()

  useEffect(() => {
    if (numId < 0) {
      showNotification({
        color: 'red',
        message: t('common.error.param_error'),
        icon: <Icon path={mdiClose} size={1} />,
      })
      navigate('/admin/games')
      return
    }

    if (gameSource) {
      setGame(gameSource)
      setStart(dayjs(gameSource.start))
      setEnd(dayjs(gameSource.end))

      const wpddl = dayjs(gameSource.writeupDeadline).diff(gameSource.end, 'h')
      setWpddl(wpddl < 0 ? 0 : wpddl)
    }
  }, [id, gameSource])

  const onUpdatePoster = async (file: File | undefined) => {
    if (!game || !file) return

    setDisabled(true)
    notifications.clean()
    showNotification({
      id: 'upload-poster',
      color: 'orange',
      message: t('admin.notification.games.info.poster.uploading'),
      loading: true,
      autoClose: false,
    })

    try {
      const res = await api.edit.editUpdateGamePoster(game.id!, { file })
      updateNotification({
        id: 'upload-poster',
        color: 'teal',
        message: t('admin.notification.games.info.poster.uploaded'),
        icon: <Icon path={mdiCheck} size={1} />,
        autoClose: true,
        loading: false,
      })
      mutate({ ...game, poster: res.data })
    } catch (err) {
      updateNotification({
        id: 'upload-poster',
        color: 'red',
        title: t('admin.notification.games.info.poster.upload_failed'),
        message: tryGetErrorMsg(err, t),
        icon: <Icon path={mdiClose} size={1} />,
        autoClose: true,
        loading: false,
      })
    } finally {
      setDisabled(false)
    }
  }

  const onUpdateInfo = async () => {
    if (!game?.title) return
    setDisabled(true)

    try {
      await api.edit.editUpdateGame(game.id!, {
        ...game,
        inviteCode: (game.inviteCode?.length ?? 0 > 6) ? game.inviteCode : null,
        start: start.valueOf(),
        end: end.valueOf(),
        writeupDeadline: end.add(wpddl, 'h').valueOf(),
      })
      showNotification({
        color: 'teal',
        message: t('admin.notification.games.info.info_updated'),
        icon: <Icon path={mdiCheck} size={1} />,
      })
      mutate()
      api.game.mutateGameGames()
    } catch (e) {
      showErrorMsg(e, t)
    } finally {
      setDisabled(false)
    }
  }

  const onConfirmDelete = async () => {
    if (!game) return

    try {
      await api.edit.editDeleteGame(game.id!)
      showNotification({
        color: 'teal',
        message: t('admin.notification.games.info.deleted'),
        icon: <Icon path={mdiCheck} size={1} />,
      })
      navigate('/admin/games')
    } catch (e) {
      showErrorMsg(e, t)
    }
  }

  const onCopyPublicKey = () => {
    clipboard.copy(game?.publicKey || '')
    showNotification({
      color: 'teal',
      message: t('admin.notification.games.info.public_key_copied'),
      icon: <Icon path={mdiCheck} size={1} />,
    })
  }

  return (
    <WithGameEditTab
      headProps={{ justify: 'apart' }}
      contentPos="right"
      isLoading={!game}
      head={
        <>
          <Button
            disabled={disabled}
            color="red"
            leftSection={<Icon path={mdiDeleteOutline} size={1} />}
            variant="outline"
            onClick={() =>
              modals.openConfirmModal({
                title: t('admin.button.games.delete'),
                children: <Text size="sm">{t('admin.content.games.info.delete', { name: game?.title })}</Text>,
                onConfirm: () => onConfirmDelete(),
                confirmProps: { color: 'red' },
              })
            }
          >
            {t('admin.button.games.delete')}
          </Button>
          <Button leftSection={<Icon path={mdiClipboard} size={1} />} disabled={disabled} onClick={onCopyPublicKey}>
            {t('admin.button.games.copy_public_key')}
          </Button>
          <Button
            leftSection={<Icon path={mdiContentSaveOutline} size={1} />}
            disabled={disabled}
            onClick={onUpdateInfo}
          >
            {t('admin.button.save')}
          </Button>
        </>
      }
    >
      <SimpleGrid cols={4}>
        <TextInput
          label={t('admin.content.games.info.title.label')}
          description={t('admin.content.games.info.title.description')}
          disabled={disabled}
          value={game?.title}
          required
          onChange={(e) => game && setGame({ ...game, title: e.target.value })}
        />
        <NumberInput
          label={t('admin.content.games.info.member_limit.label')}
          description={t('admin.content.games.info.member_limit.description')}
          disabled={disabled}
          min={0}
          required
          value={game?.teamMemberCountLimit}
          onChange={(e) => {
            const number = getInputNumber(e)
            if (!game || isNaN(number)) return
            setGame({ ...game, teamMemberCountLimit: number })
          }}
        />
        <NumberInput
          label={t('admin.content.games.info.container_limit.label')}
          description={t('admin.content.games.info.container_limit.description')}
          disabled={disabled}
          min={0}
          required
          value={game?.containerCountLimit}
          onChange={(e) => {
            const number = getInputNumber(e)
            if (!game || isNaN(number)) return
            setGame({ ...game, containerCountLimit: number })
          }}
        />
        <TextInput
          label={t('admin.content.games.info.invite_code.label')}
          description={t('admin.content.games.info.invite_code.description')}
          value={game?.inviteCode || ''}
          disabled={disabled}
          onChange={(e) => game && setGame({ ...game, inviteCode: e.target.value })}
          rightSection={
            <ActionIcon onClick={() => game && setGame({ ...game, inviteCode: randomInviteCode() })}>
              <Icon path={mdiDiceMultiple} size={0.9} />
            </ActionIcon>
          }
        />
        <DateTimePicker
          label={t('admin.content.games.info.start_time')}
          size="sm"
          value={start.toDate()}
          valueFormat="L LT"
          disabled={disabled}
          clearable={false}
          onChange={(e) => {
            const newDate = dayjs(e)
            setStart(newDate)
            if (newDate && end < newDate) {
              setEnd(newDate.add(2, 'h'))
            }
          }}
          required
        />
        <DateTimePicker
          label={t('admin.content.games.info.end_time')}
          size="sm"
          disabled={disabled}
          minDate={start.toDate()}
          value={end.toDate()}
          valueFormat="L LT"
          clearable={false}
          onChange={(e) => {
            setEnd(dayjs(e))
          }}
          error={end < start}
          required
        />
        <Switch
          disabled={disabled}
          checked={game?.acceptWithoutReview ?? false}
          classNames={{ root: misc.switchVerticalMiddle }}
          label={SwitchLabel(
            t('admin.content.games.info.accept_without_review.label'),
            t('admin.content.games.info.accept_without_review.description')
          )}
          onChange={(e) => game && setGame({ ...game, acceptWithoutReview: e.target.checked })}
        />
        <Switch
          disabled={disabled}
          checked={game?.practiceMode ?? true}
          classNames={{ root: misc.switchVerticalMiddle }}
          label={SwitchLabel(
            t('admin.content.games.info.practice_mode.label'),
            t('admin.content.games.info.practice_mode.description')
          )}
          onChange={(e) => game && setGame({ ...game, practiceMode: e.target.checked })}
        />
      </SimpleGrid>
      <Group grow justify="space-between">
        <Textarea
          label={t('admin.content.games.info.summary.label')}
          description={t('admin.content.games.info.summary.description')}
          value={game?.summary}
          w="100%"
          autosize
          disabled={disabled}
          minRows={8}
          maxRows={8}
          onChange={(e) => game && setGame({ ...game, summary: e.target.value })}
        />
        <Stack gap="0.488125rem">
          <Group grow justify="space-between">
            <Switch
              disabled={disabled}
              checked={game?.writeupRequired ?? false}
              classNames={{ root: misc.switchVerticalMiddle }}
              label={SwitchLabel(
                t('admin.content.games.info.writeup_required.label'),
                t('admin.content.games.info.writeup_required.description')
              )}
              onChange={(e) => game && setGame({ ...game, writeupRequired: e.target.checked })}
            />
            <NumberInput
              label={t('admin.content.games.info.writeup_deadline.label')}
              description={t('admin.content.games.info.writeup_deadline.description')}
              disabled={disabled}
              min={0}
              required
              value={wpddl}
              onChange={(e) => setWpddl(getInputNumber(e))}
            />
          </Group>
          <Textarea
            label={t('admin.content.games.info.writeup_instruction')}
            description={t('admin.content.markdown_support')}
            value={game?.writeupNote}
            w="100%"
            autosize
            disabled={disabled}
            minRows={4}
            maxRows={4}
            onChange={(e) => game && setGame({ ...game, writeupNote: e.target.value })}
          />
        </Stack>
      </Group>
      <Grid grow>
        <Grid.Col span={8}>
          <Textarea
            label={
              <Group gap="sm">
                <Text size="sm">{t('admin.content.games.info.content')}</Text>
                <Text size="xs" c="dimmed">
                  {t('admin.content.markdown_support')}
                </Text>
              </Group>
            }
            value={game?.content}
            w="100%"
            autosize
            disabled={disabled}
            minRows={10}
            maxRows={10}
            onChange={(e) => game && setGame({ ...game, content: e.target.value })}
          />
        </Grid.Col>
        <Grid.Col span={4}>
          <Input.Wrapper label={t('admin.content.games.info.poster')}>
            <Dropzone
              onDrop={(files) => onUpdatePoster(files[0])}
              onReject={() => {
                showNotification({
                  color: 'red',
                  title: t('common.error.file_invalid.title'),
                  message: t('common.error.file_invalid.message'),
                  icon: <Icon path={mdiClose} size={1} />,
                })
              }}
              maxSize={3 * 1024 * 1024}
              accept={IMAGE_MIME_TYPES}
              disabled={disabled}
              data-poster={game?.poster || undefined}
              classNames={{ root: misc.gamePoster }}
            >
              <Center className={misc.noPointerEvents}>
                {game?.poster ? (
                  <Image height="231px" fit="contain" src={game.poster} alt="poster" />
                ) : (
                  <Center h="231px">
                    <Stack gap={0}>
                      <Text size="xl" inline>
                        {t('common.content.drop_zone.content', {
                          type: t('common.content.drop_zone.type.poster'),
                        })}
                      </Text>
                      <Text size="sm" c="dimmed" inline mt={7}>
                        {t('common.content.drop_zone.limit')}
                      </Text>
                    </Stack>
                  </Center>
                )}
              </Center>
            </Dropzone>
          </Input.Wrapper>
        </Grid.Col>
      </Grid>
    </WithGameEditTab>
  )
}

export default GameInfoEdit
